package com.shenhonghui.photo.slice;

import com.shenhonghui.photo.ResourceTable;
import com.shenhonghui.photo.ServiceIdlInterfaceProxy;
import com.shenhonghui.photo.component.SelectDeviceDialog;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.ability.IAbilityContinuation;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.aafwk.content.Operation;
import ohos.agp.components.*;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.window.dialog.ToastDialog;
import ohos.app.Context;
import ohos.app.Environment;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.app.dispatcher.task.Revocable;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.bundle.AbilityInfo;
import ohos.bundle.ElementName;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.data.distributed.common.*;
import ohos.data.distributed.user.SingleKvStore;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.distributedschedule.interwork.DeviceInfo;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.*;
import ohos.media.image.*;
import ohos.media.image.Image;
import ohos.media.image.common.*;
import ohos.media.photokit.metadata.AVStorage;
import ohos.rpc.IRemoteObject;
import ohos.rpc.RemoteException;
import ohos.utils.net.Uri;

import java.io.*;
import java.util.*;

public class CameraAbilitySlice extends AbilitySlice implements IAbilityContinuation {
    static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP,0x00201,"Camera_TAG");
    private static Camera m_Camera = null;
    private static ImageReceiver m_ImageReceiver = null;
    private EventHandler m_EventHandler = null;
    private SurfaceProvider m_surfaceProvider = null;
    private final static int CAPTURE = 202;
    private String masterOrSlave = "master";
    private static ohos.agp.components.Image image;

    private KvManager kvManager;
    public static SingleKvStore singleKvStore;
    private String storeID = "picture";
    private static PixelMap pixelMap;

    private static Button slaveCaptureBtn;

    private static byte[] localBytes = null;
    private static byte[] remoteBytes = null;
    private static boolean savePicture = false;
    private static Context context = null;
    private static boolean firstCapture = true;

    public static EventHandler getHandler(){
        return m_Handler;
    }

    private static EventHandler m_Handler = new EventHandler(EventRunner.current()) {
        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            switch (event.eventId) {
                case CAPTURE:
                    slaveCaptureBtn.simulateClick();
                    break;
            }
        }
    };

    private ServiceIdlInterfaceProxy remoteInterfaceProxy = null;
    //创建远程连接回调实例
    private final IAbilityConnection remoteAbilityConnection = new IAbilityConnection() {
        //连接到Service的回调
        @Override
        public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
            //Client侧需要定义与Service侧相同的IRemoteObject实现类。开发者获取服务端传过来IRemoteObject对象，并从中解析出服务端传过来的信息。
            //成功连接上 i的值为0
            if(i == 0) {
                remoteInterfaceProxy = new ServiceIdlInterfaceProxy(iRemoteObject);
            }
        }
        //断开与Service连接的回调
        @Override
        public void onAbilityDisconnectDone(ElementName elementName, int i) {
            if(i == 0) {
                remoteInterfaceProxy = null;
            }
        }
    };

    private final ImageReceiver.IImageArrivalListener iImageArrivalListener = new ImageReceiver.IImageArrivalListener() {
        @Override
        public void onImageArrival(ImageReceiver imageReceiver) {
            StringBuffer fileName = new StringBuffer("picture_");
            fileName.append(UUID.randomUUID()).append(".jpg");
            File dirFile = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            if(!dirFile.exists()) {
                dirFile.mkdir();
            }

            ImageSaver imageSaver = new ImageSaver(imageReceiver.readNextImage(), fileName.toString());
            if(m_EventHandler != null) {
                m_EventHandler.postTask(imageSaver);
            }
        }
    };

    private SurfaceOps.Callback callback = new SurfaceOps.Callback() {
        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {
            HiLog.info(label, "创建surface回调surfaceCreated() called.");
            CameraKit cameraKit = CameraKit.getInstance(getContext());
            if(cameraKit == null) {
                return;
            }

            String[] cameraIdList = cameraKit.getCameraIds();
            if(cameraIdList.length <= 0 ) {
                HiLog.info(label,"cameraIdList size 0.");
            }

            CameraStateCallback cameraStateCallback = new CameraStateCallback() {
                @Override
                public void onCreated(Camera camera) {
                    super.onCreated(camera);
                    HiLog.info(label, "创建相机状态回调 camera created.");
                    CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
                    if(cameraConfigBuilder == null) {
                        HiLog.info(label, "cameraConfigBuilder is null");
                        return;
                    }
                    m_Camera = camera;
                    cameraConfigBuilder.addSurface(surfaceOps.getSurface());
                    cameraConfigBuilder.addSurface(m_ImageReceiver.getRecevingSurface());
                    camera.configure(cameraConfigBuilder.build());
                }

                @Override
                public void onCreateFailed(String cameraId, int errorCode) {
                    super.onCreateFailed(cameraId, errorCode);
                    HiLog.info(label, "相机状态错误回调 camera create failed. errorCode " + errorCode);
                }

                @Override
                public void onConfigured(Camera camera) {
                    super.onConfigured(camera);
                    FrameConfig.Builder frameConfigBuilder = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
                    frameConfigBuilder.addSurface(surfaceOps.getSurface());
                    FrameConfig frameConfig = frameConfigBuilder.build();
                    int triggerId = camera.triggerLoopingCapture(frameConfig);
                }
            };
            m_EventHandler = new EventHandler(EventRunner.current());
            m_ImageReceiver = ImageReceiver.create(1920,1080, ImageFormat.JPEG, 1);
            m_ImageReceiver.setImageArrivalListener(iImageArrivalListener);
            cameraKit.createCamera(cameraIdList[0], cameraStateCallback, m_EventHandler);
        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) {
            HiLog.info(label, "surfacechange回调surfaceChanged() called.");
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
            HiLog.info(label, "surfaceDestroyed() called.");
            if(m_Camera != null) {
                m_Camera.release();
                m_Camera = null;
            }
        }
    };

    private Component.ClickedListener clickedListener = new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            switch (component.getId()) {
                case ResourceTable.Id_continueAbility:
                    try {
                        SelectDeviceDialog selectDeviceDialog = new SelectDeviceDialog(CameraAbilitySlice.this, new SelectDeviceDialog.SelectResultListener() {
                            @Override
                            public void callBack(DeviceInfo deviceInfo) {
                                Operation remoteOperation = new Intent.OperationBuilder()
                                        .withDeviceId(deviceInfo.getDeviceId())
                                        .withBundleName("com.shenhonghui.photo")
                                        .withAbilityName("com.shenhonghui.photo.ServiceAbility")
                                        .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                                        .build();
                                Intent remoteIntent = new Intent();
                                remoteIntent.setOperation(remoteOperation);
                                if(connectAbility(remoteIntent, remoteAbilityConnection)) HiLog.info(label,"remote connectAbility success" + deviceInfo.getDeviceName());
                                else HiLog.info(label,"remote connectAbility failed");
                                continueAbility(deviceInfo.getDeviceId());
                            }
                        });
                        selectDeviceDialog.show();
                    } catch(IllegalStateException e){
                        e.getMessage();
                    }
                    break;
                case ResourceTable.Id_slaveCapture:
                    capture();
                    break;
                case ResourceTable.Id_masterCapture:
                    if(firstCapture) {
                        if(remoteInterfaceProxy != null) {
                            try {
                                remoteInterfaceProxy.capture();
                                firstCapture = false;
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                    }else {
                        try {
                            if(remoteInterfaceProxy != null) {
                                remoteInterfaceProxy.capture();
                            }
                            localBytes = null;
                            savePicture = true;
                            firstCapture = true;
                            capture();
                        } catch (Exception e) {
                            e.getMessage();
                        }
                    }

                    break;
                case ResourceTable.Id_selectPhotos:
                    Intent photosIntent = new Intent();
                    Operation operation = new Intent.OperationBuilder()
                            .withAbilityName("com.shenhonghui.photo.PictureSetAbility")
                            .withBundleName("com.shenhonghui.photo")
                            .withDeviceId("")
                            .withFlags(Intent.FLAG_START_FOREGROUND_ABILITY)
                            .build();
                    photosIntent.setOperation(operation);
                    startAbility(photosIntent);
                    break;
            }
        }
    };

    //流转
    @Override
    public boolean onStartContinuation() {
        return true;
    }

    @Override
    public boolean onSaveData(IntentParams intentParams) {
        intentParams.setParam("masterOrSlave","slave");
        return true;
    }

    @Override
    public boolean onRestoreData(IntentParams intentParams) {
        masterOrSlave = intentParams.getParam("masterOrSlave").toString();
        return true;
    }

    @Override
    public void onCompleteContinuation(int i) {
    }

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        if(masterOrSlave.equals("master")) {
            super.setUIContent(ResourceTable.Layout_ability_master);
        }else {
            super.setUIContent(ResourceTable.Layout_ability_slave);
        }
        setDisplayOrientation(AbilityInfo.DisplayOrientation.LANDSCAPE);
        context = CameraAbilitySlice.this;
        KvManagerConfig config = new KvManagerConfig(getContext());
        kvManager = KvManagerFactory.getInstance().createKvManager(config);

        Options CREATE = new Options();
        CREATE.setCreateIfMissing(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
        CREATE.setAutoSync(true);
        singleKvStore = kvManager.getKvStore(CREATE, storeID);

        if(masterOrSlave.equals("master")) {
            Button continueBtn = (Button) findComponentById(ResourceTable.Id_continueAbility);
            Button masterCaptureBtn= (Button) findComponentById(ResourceTable.Id_masterCapture);
            Button selectBtn = (Button) findComponentById(ResourceTable.Id_selectPhotos);

            image = (ohos.agp.components.Image)findComponentById(ResourceTable.Id_slaveImage);

            continueBtn.setClickedListener(clickedListener);
            masterCaptureBtn.setClickedListener(clickedListener);
            selectBtn.setClickedListener(clickedListener);

            m_surfaceProvider = (SurfaceProvider)findComponentById(ResourceTable.Id_masterSurface);
            m_surfaceProvider.pinToZTop(false);
            m_surfaceProvider.getSurfaceOps().get().addCallback(callback);
            getWindow().setTransparent(true);

            KvStoreObserver kvStoreObserverClient = new KvStoreObserverClient();
            singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, kvStoreObserverClient);

        }else {
            slaveCaptureBtn = (Button) findComponentById(ResourceTable.Id_slaveCapture);
            slaveCaptureBtn.setClickedListener(clickedListener);

            m_surfaceProvider = (SurfaceProvider)findComponentById(ResourceTable.Id_slaveSurface);
            m_surfaceProvider.pinToZTop(false);
            m_surfaceProvider.getSurfaceOps().get().addCallback(callback);
            getWindow().setTransparent(true);
        }
    }

    public class KvStoreObserverClient implements KvStoreObserver {
        @Override
        public void onChange(ChangeNotification changeNotification) {
            remoteBytes = singleKvStore.getByteArray("imageBytes");
            if(savePicture == false) {
                TaskDispatcher uiTaskDispatcher = getUITaskDispatcher();
                Revocable revocable = uiTaskDispatcher.asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                        ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
                        srcOpts.formatHint = "image/jpeg";
                        ImageSource imageSource = ImageSource.create(remoteBytes,srcOpts);
                        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
                        decodingOptions.desiredRegion = new Rect(1440, 0, 480, 1080);
                        decodingOptions.desiredSize = new Size(image.getWidth(), image.getHeight());
                        pixelMap = imageSource.createPixelmap(decodingOptions);
                        imageSource.release();
                        image.release();
                        if(pixelMap != null) {
                            image.setPixelMap(pixelMap);
                            pixelMap.release();
                        }
                        remoteBytes = null;
                    }
                });
                savePicture = true;
            }else {
                TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
                Revocable revocable = globalTaskDispatcher.asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                        ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
                        srcOpts.formatHint = "image/jpeg";
                        ImageSource localImageSource = ImageSource.create(localBytes,srcOpts);
                        PixelMap localPixelMap = localImageSource.createPixelmap(null);
                        ImageSource remoteImageSource = ImageSource.create(remoteBytes, srcOpts);
                        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
                        decodingOptions.desiredRegion = new Rect(1440, 0, 480, 1080);
                        PixelMap remotePixelMap = remoteImageSource.createPixelmap(decodingOptions);
                        localImageSource.release();
                        remoteImageSource.release();

                        int[] localPixel = new int[1920*1080];
                        int[] remotePixel = new int[480*1080];
                        localPixelMap.readPixels(localPixel,0,1920,new Rect(0,0,1920,1080));
                        remotePixelMap.readPixels(remotePixel,0,480,new Rect(0,0,480,1080));
                        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
                        initializationOptions.size = new Size(2400,1080);
                        initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
                        initializationOptions.alphaType = AlphaType.OPAQUE;
                        initializationOptions.editable = true;
                        PixelMap pixelMap = PixelMap.create(initializationOptions);
                        pixelMap.writePixels(localPixel,0, 1920, new Rect(0,0,1920,1080));
                        pixelMap.writePixels(remotePixel,0,480,new Rect(1920,0,480,1080));
                        StringBuffer fileName = new StringBuffer("picture_");
                        fileName.append(UUID.randomUUID()).append(".jpg");

                        DataAbilityHelper helper = DataAbilityHelper.creator(context);
                        FileDescriptor fileDescriptor = null;
                        FileOutputStream outputStream = null;
                        try {
                            ValuesBucket value = new ValuesBucket();
                            value.putString(AVStorage.Images.Media.DISPLAY_NAME, fileName.toString());
                            int result = helper.insert(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,value);
                            if (result != 1) {
                                HiLog.info(label, "failed to insert result:" + result);
                            }
                            //--- 查询
                            ResultSet set = null;
                            Uri uri = null;

                            DataAbilityPredicates predicates = new DataAbilityPredicates("_display_name = '"+fileName+"'");
                            set = helper.query(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{AVStorage.Images.Media.ID}, predicates);
                            if (set != null && set.goToFirstRow()) {
                                int columnId = set.getColumnIndexForName(AVStorage.Images.Media.ID);
                                int mediaId = set.getInt(columnId);
                                uri = ohos.utils.net.Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, "" + mediaId);
                                fileDescriptor = helper.openFile(uri, "rw");
                            }
                            outputStream = new FileOutputStream(fileDescriptor);
                            ImagePacker imagePacker = ImagePacker.create();
                            ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
                            packingOptions.format = "image/jpeg";
                            packingOptions.quality = 90;
                            imagePacker.initializePacking(outputStream, packingOptions);
                            imagePacker.addImage(pixelMap);
                            imagePacker.finalizePacking();
                            imagePacker.release();
                            outputStream.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }finally {
                            localBytes = null;
                            remoteBytes = null;
                        }
                    }
                });
            }
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        HiLog.info(label, "onStop 被调用");
        m_surfaceProvider.removeFromWindow();
        if(remoteAbilityConnection != null) {
            disconnectAbility(remoteAbilityConnection);
        }
        kvManager.closeKvStore(singleKvStore);
        if(m_Camera != null) {
            m_Camera.release();
            m_Camera = null;
        }
    }

    private class ImageSaver implements Runnable {
        private final Image imageJEPG;
        private final String fileName;

        private ImageSaver(Image image, String fileName) {
            this.imageJEPG = image;
            this.fileName = fileName;
        }

        @Override
        public void run() {
            Image.Component component = imageJEPG.getComponent(ImageFormat.ComponentType.JPEG);
            localBytes = new byte[component.remaining()];
            component.read(localBytes);
            if(masterOrSlave.equals("slave")) {
                singleKvStore.putByteArray("imageBytes", localBytes);
            }
            DataAbilityHelper helper = DataAbilityHelper.creator(CameraAbilitySlice.this);
            FileDescriptor fileDescriptor = null;
            FileOutputStream outputStreamPublic = null;
            try {
                ValuesBucket value = new ValuesBucket();
                value.putString(AVStorage.Images.Media.DISPLAY_NAME, fileName);
                int result = helper.insert(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,value);
                if (result != -1) {
                    HiLog.info(label, "failed to insert result:" + result);
                }
                ResultSet set = null;
                Uri uri = null;
                DataAbilityPredicates predicates = new DataAbilityPredicates("_display_name = '" + fileName + "'");
                set = helper.query(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{AVStorage.Images.Media.ID}, predicates);
                if (set != null && set.goToFirstRow()) {
                    int columnId = set.getColumnIndexForName(AVStorage.Images.Media.ID);
                    int mediaId = set.getInt(columnId);
                    uri = ohos.utils.net.Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, "" + mediaId);
                    fileDescriptor = helper.openFile(uri, "rw");
                }
                outputStreamPublic = new FileOutputStream(fileDescriptor);
                outputStreamPublic.write(localBytes);
            }catch (Exception e){
                e.printStackTrace();
            } finally {
                imageJEPG.release();
                try {
                    outputStreamPublic.close();
                    ToastDialog toastDialog = new ToastDialog(CameraAbilitySlice.this);
                    toastDialog.setText("拍照成功");
                    toastDialog.show();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //调用拍照
    private static void capture() {
        HiLog.info(label, "capture() called.");
        //获取拍照配置模板
        FrameConfig.Builder pictureFrameConfigBuilder = m_Camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PICTURE);
        //配置拍照surface
        pictureFrameConfigBuilder.addSurface(m_ImageReceiver.getRecevingSurface());
        try {
            //启动拍照
            m_Camera.triggerSingleCapture(pictureFrameConfigBuilder.build());
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}


